using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Resources;
using System.Reflection;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Component;
using Microsoft.BizTalk.Messaging;
using System.Xml;
using System.Collections.Generic;
using System.Xml.Serialization;
using Sixeyed.CacheAdapter.Configuration;
using Sixeyed.CacheAdapter.Management;
using Sixeyed.CacheAdapter.Interfaces;
using Sixeyed.CacheAdapter.Logging;
using Sixeyed.CacheAdapter.Helpers;
using System.Drawing.Design;

namespace Sixeyed.CacheAdapter.PipelineComponents
{    
    /// <summary>
    /// Receive pipeline component which checks if the incoming message has
    /// a response stored in the configured cache provider
    /// </summary>
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_Any)]
    [System.Runtime.InteropServices.Guid("3A0DD74D-3BF5-48fa-A948-B59E4D743BCE")]
    public class CheckIsCached : PipelineComponentBase
    {
        #region Private instance fields

        private string _providerTypeName;
        private string _providerConfiguration;

        #endregion

        #region Design Time Properties

        /// <summary>
        /// Fully-qualified type name of the cache provider
        /// </summary>
        [Browsable(true)]
        [Description("Fully-qualified type name of the cache provider")]
        [Editor(typeof(Sixeyed.CacheAdapter.Management.UITypeEditors.CacheProviderTypeName), typeof(UITypeEditor))]
        public string CacheProviderTypeName
        {
            get { return _providerTypeName; }
            set { _providerTypeName = value; }
        }

        /// <summary>
        /// Configuration of the cache instance
        /// </summary>
        [Browsable(true)]
        [Description("Configuration of the cache instance")]
        [Editor(typeof(Sixeyed.CacheAdapter.Management.UITypeEditors.CacheProviderConfiguration), typeof(UITypeEditor))]
        public string CacheProviderConfiguration
        {
            get { return _providerConfiguration; }
            set { _providerConfiguration = value; }
        }

        #endregion

        private static object _syncLock = new object();
        private static Dictionary<string, ICacheProvider> _cacheProviders;
        private static Dictionary<string, ICacheProvider> CacheProviders
        {
            get
            {
                if (_cacheProviders == null)
                {
                    lock (_syncLock)
                    {
                        if (_cacheProviders == null)
                        {
                            _cacheProviders = new Dictionary<string, ICacheProvider>();
                        }
                    }
                }
                return _cacheProviders;
            }
        }

        private ICacheProvider GetCacheProvider()
        {
            if (!CacheProviders.ContainsKey(this.CacheProviderConfiguration))
            {
                TransmitLocation location = new TransmitLocation();
                location.Provider = this.CacheProviderTypeName;
                location.Configuration = this.CacheProviderConfiguration;
                ICacheProviderConfiguration config = location.GetCacheConfiguration();
                ICacheProvider provider = CacheFactory.GetProvider(location.Provider, config);
                if (provider != null)
                {
                    CacheProviders.Add(this.CacheProviderConfiguration, provider);
                }
            }
            return CacheProviders[this.CacheProviderConfiguration];
        }

        #region PipelineComponentBase overrides

        /// <summary>
        /// Gets the GUID for this component
        /// </summary>
        protected override Guid Guid
        {
            get { return new Guid("3A0DD74D-3BF5-48fa-A948-B59E4D743BCE"); }
        }

        /// <summary>
        /// Populates cache configuration as contetx properties on message
        /// </summary>
        /// <param name="pc">Pipeline context</param>
        /// <param name="inmsg">Incoming message</param>
        /// <returns>Incoming message with context properties</returns>
        public override IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg)
        {
            MessageCacheProperties properties = MessageCacheProperties.LoadFromMessage(inmsg);
            if (!string.IsNullOrEmpty(this.CacheProviderTypeName) && !string.IsNullOrEmpty(this.CacheProviderConfiguration))
            {
                if (properties.IsMessageCacheEnabled)
                {
                    ICacheProvider provider = this.GetCacheProvider();                    

                    //get the cache key:
                    if (string.IsNullOrEmpty(properties.MessageCacheKey))
                    {
                        Stream dataStream = inmsg.BodyPart.GetOriginalDataStream();
                        if (dataStream != null && dataStream.CanRead)
                        {
                            byte[] objectBuffer = StreamHelper.GetBytes(dataStream);
                            properties.MessageCacheKey = provider.GetCacheKey(objectBuffer);
                        }
                        if (dataStream.CanSeek)
                        {
                            dataStream.Position = 0;
                        }
                    }

                    if (!string.IsNullOrEmpty(properties.MessageCacheKey))
                    {
                        //TODO - replace with ContainsKey on cache provider:
                        byte[] response = provider.Get<byte[]>(properties.MessageCacheKey);
                        properties.IsCached = (response != null);
                    }

                    //write properties:
                    MessageCacheProperties.Save(inmsg, properties, provider);
                }
            }
            else
            {
                Logger.Log(LogLevel.Error, "Sixeyed.CacheAdapter.CheckIsCached - TransmitLocation not specified");
            }

            return inmsg;
        }

        #endregion

        #region Overidden members

        /// <summary>
        /// Loads configuration properties for the component
        /// </summary>
        /// <param name="propertyBag">Configuration property bag</param>
        /// <param name="errlog">Error status</param>
        public override void Load(IPropertyBag propertyBag, int errlog)
        {
            object value = ReadPropertyBag(propertyBag, PropertyName.CacheProviderConfiguration);
            if (value != null && value is string)
            {
                this.CacheProviderConfiguration = (string)value;
            }
            value = ReadPropertyBag(propertyBag, PropertyName.CacheProviderTypeName);
            if (value != null && value is string)
            {
                this.CacheProviderTypeName = (string)value;
            }
        }

        /// <summary>
        /// Saves the current component configuration into the property bag
        /// </summary>
        /// <param name="propertyBag">Configuration property bag</param>
        /// <param name="clearDirty">not used</param>
        /// <param name="saveAllProperties">not used</param>
        public override void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
        {
            WritePropertyBag(propertyBag, PropertyName.CacheProviderConfiguration, this.CacheProviderConfiguration);
            WritePropertyBag(propertyBag, PropertyName.CacheProviderTypeName, this.CacheProviderTypeName);
        }

        #endregion

        private struct PropertyName
        {
            public const string CacheProviderTypeName = "CacheProviderTypeName";
            public const string CacheProviderConfiguration = "CacheProviderConfiguration";
        }
    }
}


